/*
 * spi_frame.c
 *
 *  Created on: 3 janv. 2014
 *      Author: crma
 */

#include "spi/spi_frame.h"
#include "hcpp/ptp2_slave.h"
#include "hcpp/ptp2_node_address.h"



static uint16_t spi_seq_number_getnext(void);
static bool     spi_frame_header_encode(spi_frame_type_t frame_type,
                                        spi_payload_t *payload,
                                        spi_frame_header_t *spi_header);

static uint16_t spi_checksum_compute(uint8_t *payload, uint8_t payloadLen);
static bool     slave_record_payload_encode(uint8_t *raw_data, spi_payload_t *payload);



typedef struct{
  spi_master_frame_builder  builder;
  spi_frame_type_t          type;
  uint8_t                   payload_len;
}builder_dic_t;



builder_dic_t builder_dic[]={
    {slave_record_encoder, eSpiSlaveRecordSet, 76}      // 70 if expire uint32
};


/***
static void
spi_frame_destructor(void *ptr)
{
#if PTP2_LOG
    spi_frame_t *this = ptr;
    PTP2_LOG_PRINTLN(PTP2_LOG_PACKET, PTP2_LOG_WARNING, ("spi_frame_destructor(%08X)", (unsigned int) this ));
#endif
}


static class_info_t class_info = {
    .size        = sizeof(spi_frame_t),
    .destructor  = spi_frame_destructor,
    .mem_alloc   = malloc,
    .mem_free    = free
};
*/


/**
 * The function encodes the slave record into a SPI frame.
 *
 *
 * @param   void *: raw_data
 * @param   uint8_t: raw_data length
 * @param   spi_frame_type_t: frame_type (shall be eSpiSlaveRecordSet in this case)
 * @param   spi_frame_t: *frame output frame
 * @return  bool true if successful
 *
 */
bool
slave_record_encoder(void *raw_data,
                          uint8_t length,
                          spi_frame_type_t frame_type,
                          spi_frame_t *frame)
{
  bool                  result = false;


  if(NULL != raw_data && NULL != frame){

    result = slave_record_payload_encode(raw_data, &frame->payload);
    if(false !=result){
      result = spi_frame_header_encode(frame_type, &frame->payload, &frame->header);
    }
  }


  return result;
}





bool
slave_record_payload_encode(uint8_t *raw_data, spi_payload_t *payload)
{
  bool result = false;
  uint8_t       *dst;


  if(NULL != raw_data && NULL != payload){

    dst = (uint8_t*)payload;
    ptp2_slave_t *slave_record = (ptp2_slave_t *)raw_data;

    payload->slave_record = *slave_record;

    /****
    *(uint16_t*)dst = slave_record->position;
    dst += sizeof(uint16_t);

    *(uint8_t*)dst = (uint8_t)slave_record->address->type;
    dst += sizeof(uint8_t);

    switch(slave_record->address->type){
    case PTP2_ADDRESS_LAYER2:
      memcpy((uint8_t*)dst, (uint8_t*)&slave_record->address->mac, sizeof(ptp2_node_address_t));
      break;
    case PTP2_ADDRESS_IPV4:
      memcpy((uint8_t*)dst, (uint8_t*)&slave_record->address->ipv4, sizeof(ptp2_node_address_t));
      break;
    case PTP2_ADDRESS_IPV6:
      memcpy((uint8_t*)dst, (uint8_t*)&slave_record->address->ipv6, sizeof(ptp2_node_address_t));
      break;
    default:
      break;
    }
    dst += sizeof(ptp2_node_address_t);

    memcpy((uint8_t*)dst, (uint8_t*)&slave_record->port_identity, sizeof(ptp2_port_identity_t));
    dst += sizeof(ptp2_port_identity_t);

    *(uint16_t*)dst = (uint16_t)slave_record->seq_id;
    dst += sizeof(uint16_t);

    *(int8_t*)dst = (int8_t)slave_record->sync_log_period;
    dst += sizeof(uint8_t);

    *(uint32_t*)dst = (uint32_t)slave_record->sync_duration;
    dst += sizeof(uint32_t);

    *(uint64_t*)dst = (uint64_t)slave_record->sync_expire;
    dst += sizeof(uint64_t);

    *(int8_t*)dst = (int8_t)slave_record->announce_log_period;
    dst += sizeof(uint8_t);

    *(uint32_t*)dst = (uint32_t)slave_record->announce_duration;
    dst += sizeof(uint32_t);

    *(uint64_t*)dst = (uint64_t)slave_record->announce_expire;
    dst += sizeof(uint64_t);

    *(int8_t*)dst = (int8_t)slave_record->delay_response_log_period;
    dst += sizeof(uint8_t);

    *(uint32_t*)dst = (uint32_t)slave_record->delay_response_duration;
    dst += sizeof(uint32_t);

    *(uint64_t*)dst = (uint64_t)slave_record->delay_response_expire;
    dst += sizeof(uint64_t);

    *(uint8_t*)dst = (uint8_t)0xFF; // padding
    dst += sizeof(uint8_t);
    ****/
    result = true;
  }
  return result;
}

/**
 * The function formats the frame content according to the type of message
 *
 *
 * @param   spi_frame_t: ref
 * @param   raw_data: input raw data to be formatted
 * @param   prio: input priority associated to the message
 * @return  bool true if successful
 *
 */
bool
spi_frame_encode(void *raw_data, uint8_t length, spi_frame_type_t frame_type, spi_frame_t *frame)
{
  bool                      result = false;
  spi_master_frame_builder  builder = NULL;
  uint8_t                   i;

  for(i=0; i < sizeof(builder_dic)/sizeof(builder_dic[0]); i++){
    if(builder_dic[i].type==frame_type){
      builder=builder_dic[i].builder;
      return result = builder(raw_data, length, frame_type, frame);
    }
  }

  return result;
}

/**
 * The function formats the SPI header
 *
 *
 * @param   spi_frame_type_t: frame_type
 * @param   spi_payload_t *payload: frame content
 * @param   spi_frame_header_t *spi_header: pointer to the output header
 * @return  bool true if successful
 *
 */
bool
spi_frame_header_encode(spi_frame_type_t frame_type, spi_payload_t *payload, spi_frame_header_t *spi_header)
{
  bool          result= false;

  if(NULL !=spi_header){

    spi_header->payloadLen = 0xFF;  // 0xFF undefined
    for(uint8_t i=0; i < sizeof(builder_dic)/sizeof(builder_dic[0]); i++){
      if(builder_dic[i].type==frame_type){
        spi_header->payloadLen = builder_dic[i].payload_len;

        spi_header->dstAddress = SLAVE_TABLE_HOST_ADDRESS;
        spi_header->eType = frame_type;
        spi_header->protocolVer = SPI_PROT_VERSION;
        spi_header->seqNumber = spi_seq_number_getnext();
        spi_header->checkSum = spi_checksum_compute((uint8_t*)payload, spi_header->payloadLen);

        result = true;
      }
    }
  }
  return result;
}

uint16_t spi_seq_number_getnext(void)
{
  static uint16_t seq_number = 0;

  return((++seq_number) % 0xFFFF);

}


uint16_t
spi_checksum_compute(uint8_t *payload, uint8_t payloadLen)
{
  uint16_t  checksum=0;

  for(uint8_t i=0; i < payloadLen; i++){
    checksum ^= *payload;
    payload++;
  }

  return checksum;
}

// EOF
